﻿namespace Nomo.UnityCoreModule.Enumerators
{
    /// <summary>
    /// 表示经过包装的枚举器。设计此类的唯一意图是让此类的实例作为传递给 <see cref="UnityEngine.MonoBehaviour.StartCoroutine(System.Collections.IEnumerator)"/> 方法的参数，而不是作为正常的集合枚举器。
    /// </summary>
    public sealed class WrappedEnumerator : System.Collections.IEnumerator
    {
        private          object                         _waitObject;
        private readonly float                          _delay;
        private readonly bool                           _realtime;
        private readonly System.Collections.IEnumerator _enumerator;
        private readonly System.Action                  _action;

        /// <summary>
        /// 初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        public WrappedEnumerator() : this(0F, false, null, null) { }

        /// <summary>
        /// 使用指定的要执行的方法初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="action">要执行的方法。</param>
        public WrappedEnumerator(System.Action action) : this(0F, false, null, action) { }

        /// <summary>
        /// 使用指定的延迟时间、在延迟结束后要执行的方法初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="action">在延迟结束后要执行的方法。</param>
        public WrappedEnumerator(float delay, System.Action action) : this(delay, false, null, action) { }

        /// <summary>
        /// 使用指定的延迟时间、是否使用真实时间来计算延迟、在延迟结束后要执行的方法初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="realtime">是否使用真实时间来计算延迟。</param>
        /// <param name="action">在延迟结束后要执行的方法。</param>
        public WrappedEnumerator(float delay, bool realtime, System.Action action) : this(delay, realtime, null, action) { }

        /// <summary>
        /// 使用指定的要执行的协程初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="enumerator">要执行的协程。</param>
        public WrappedEnumerator(System.Collections.IEnumerator enumerator) : this(0F, false, enumerator, null) { }

        /// <summary>
        /// 使用指定的延迟时间、在延迟结束后要执行的协程初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="enumerator">在延迟结束后要执行的协程。</param>
        public WrappedEnumerator(float delay, System.Collections.IEnumerator enumerator) : this(delay, false, enumerator, null) { }

        /// <summary>
        /// 使用指定的延迟时间、在延迟结束后要执行的协程、在协程结束后要执行的方法初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="enumerator">在延迟结束后要执行的协程。</param>
        /// <param name="action">在协程结束后要执行的方法。</param>
        public WrappedEnumerator(float delay, System.Collections.IEnumerator enumerator, System.Action action) : this(delay, false, enumerator, action) { }

        /// <summary>
        /// 使用指定的延迟时间、是否使用真实时间来计算延迟、在延迟结束后要执行的协程初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="realtime">是否使用真实时间来计算延迟。</param>
        /// <param name="enumerator">在延迟结束后要执行的协程。</param>
        public WrappedEnumerator(float delay, bool realtime, System.Collections.IEnumerator enumerator) : this(delay, realtime, enumerator, null) { }

        /// <summary>
        /// 使用指定的延迟时间、是否使用真实时间来计算延迟、在延迟结束后要执行的协程、在协程结束后要执行的方法初始化 <see cref="WrappedEnumerator"/> 类的新实例。
        /// </summary>
        /// <param name="delay">延迟时间。</param>
        /// <param name="realtime">是否使用真实时间来计算延迟。</param>
        /// <param name="enumerator">在延迟结束后要执行的协程。</param>
        /// <param name="action">在协程结束后要执行的方法。</param>
        public WrappedEnumerator(float delay, bool realtime, System.Collections.IEnumerator enumerator, System.Action action)
        {
            _delay    = delay;
            _realtime = realtime;
            SetWaitObject();
            _enumerator = enumerator;
            _action     = action;
        }

        private void SetWaitObject()
        {
            if (_delay <= 0F)
            {
                return;
            }
            switch (_realtime)
            {
                case false:
                    _waitObject = new UnityEngine.WaitForSeconds(_delay);
                    break;
                default:
                    _waitObject = new UnityEngine.WaitForSecondsRealtime(_delay);
                    break;
            }
        }

        bool System.Collections.IEnumerator.MoveNext()
        {
            switch (_waitObject)
            {
                case null:
                    switch (_enumerator != null && _enumerator.MoveNext())
                    {
                        case true:
                            return true;
                        default:
                            _action?.Invoke();
                            return false;
                    }
                default:
                    return true;
            }
        }

        object System.Collections.IEnumerator.Current
        {
            get
            {
                switch (_waitObject)
                {
                    case null:
                        return _enumerator.Current;
                    default:
                        var waitObject = _waitObject;
                        _waitObject = null;
                        return waitObject;
                }
            }
        }

        void System.Collections.IEnumerator.Reset()
        {
            _enumerator?.Reset();
            SetWaitObject();
        }
    }
}
